/* Hawk - An OSGI Based Test Framework
 *
 * Copyright (C) 2010 http://code.google.com/p/hawk-framework/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
package org.hawk.core.xml;

import java.io.IOException;
import java.util.Collection;
import java.util.Vector;


import org.hawk.core.model.Bundle;
import org.hawk.core.model.File;
import org.hawk.core.model.Input;
import org.hawk.core.model.Output;
import org.hawk.core.model.Session;
import org.hawk.core.model.Step;
import org.hawk.core.model.TestCase;
import org.hawk.core.model.Value;
import org.hawk.core.model.Variable;
import org.hawk.core.util.DOMUtil;
import org.hawk.core.util.FileIO;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * This class is responsible to parse the test session in XML file and create a 
 * object model in memory 
 * 
 * @author Rodrigo Prestes Machado
 * @version November 15, 2009
 */
public class TestSessionParser
{
	/** The XML document model */
	private Document xml;
	
	/**
	 * constructor
	 * 
	 * @param The file path 
	 * @throws SAXException
	 * @throws IOException
	 */
	public TestSessionParser(String filePath) throws SAXException, IOException {
		System.out.println(filePath);
		this.xml = DOMUtil.createDocument(FileIO.readFile(filePath));
	}
	
	/**
	 * Method responsible to parse the XML file and return the object model in memory
	 * 
	 * @return Returns the object model in memory
	 */
	public Session parse()
	{
		// Creates a test session object in memory to be executed
		Session session = new Session();
		// Step 1: parse the test cases elements
		Collection<TestCase> testCases = parseTestCaseElements();
		session.setTestCases(testCases);
		return session;
	}
	
	/**
	 * This method is responsible to parse the XML nodes about test case 
	 * 
	 * @return A collection of test case object according the object model. 
	 * Remember, a test case object is a composition of steps objects 
	 */
	private Collection<TestCase> parseTestCaseElements()
	{
		Collection<TestCase> testCaseCollection = new Vector<TestCase>();
		NodeList testCasesList = xml.getElementsByTagName("testcase");
		for (int i=0; i < testCasesList.getLength(); i++)
		{
			TestCase testcase = new TestCase();
			
			Node testCase = testCasesList.item(i);
			if (testCase.hasChildNodes())
			{
				NodeList testCaseChildList = testCase.getChildNodes();
				for (int j=0; j < testCaseChildList.getLength(); j++)
				{
					Node testCaseChild = testCaseChildList.item(j);
					
					if (testCaseChild.getLocalName() != null)
					{
						if (testCaseChild.getLocalName().equals("name"))
							testcase.setName(DOMUtil.getNodeValue(testCaseChild));
						
						if (testCaseChild.getLocalName().equals("description"))
							testcase.setDescription(DOMUtil.getNodeValue(testCaseChild));
						
						if (testCaseChild.getLocalName().equals("step"))
							testcase.addStep(parseActionElements(testCaseChild));
					}
				}
			}
			testCaseCollection.add(testcase);
		}
		return testCaseCollection;
	}
	
	/**
	 * This method is used to parse the step elements into Hawk Test configuration
	 * 
	 * @param A XML node with step information
	 * @return A step object in the object model 
	 */
	private Step parseActionElements(Node stepNode)
	{
		Step step = new Step();
		if (stepNode.hasChildNodes())
		{
			NodeList actionChildList = stepNode.getChildNodes();
			for (int x=0; x < actionChildList.getLength(); x++)
			{
				Node actionChild = actionChildList.item(x);
				if (actionChild.getLocalName() != null)
				{
					if (actionChild.getLocalName().equals("description"))
						step.setDescription(DOMUtil.getNodeValue(actionChild));
					
					if (actionChild.getLocalName().equals("bundle"))
						step.setBundle(parseBundleElement(actionChild));
				}
			}
		}
		return step;	
	}

	/**
	 * This method is used to parse the XML bundle nodes into Open Hawk Test
	 * configuration. According the model, a bundle object is formed by inputs 
	 * and outputs objects
	 * 
	 * @param A XML node with bundle information
	 * @return A bundle object 
	 */
	private Bundle parseBundleElement(Node bundleNode)
	{
		Bundle bundle = new Bundle();
		if (bundleNode.hasChildNodes())
		{
			NodeList bundleChildList = bundleNode.getChildNodes();
			for (int i=0; i < bundleChildList.getLength(); i++)
			{
				Node bundleChild = bundleChildList.item(i);
				if (bundleChild.getLocalName() != null)
				{
					if (bundleChild.getLocalName().equals("input"))
						bundle.setInput(parseInputElement(bundleChild));
					
					if (bundleChild.getLocalName().equals("output"))
						bundle.setOutput(parseOutputElement(bundleChild));
				}
			}
		}
		
		if (bundleNode.hasAttributes()) {
			NamedNodeMap bundleAttributesList = bundleNode.getAttributes();
			Node nameAttribute = bundleAttributesList.getNamedItem("serviceName");
			bundle.setServiceName(DOMUtil.getNodeValue(nameAttribute));
		}
		return bundle;	
	}

	/**
	 * This method is used to parse the XML output nodes into Hawk Test configuration
	 * 
	 * @param  A XML node with output information
	 * @return An output object 
	 */
	private Output parseOutputElement(Node outputNode)
	{
		Output output = new Output();
		if (outputNode.hasChildNodes())
		{
			NodeList outputChildList = outputNode.getChildNodes();
			for (int i=0; i < outputChildList.getLength(); i++)
			{
				Node outputChild = outputChildList.item(i);
				if (outputChild.getLocalName() != null)
				{
					if (outputChild.getLocalName().equals("variable"))
						output.setVariable(DOMUtil.getNodeValue(outputChild));
					
					if (outputChild.getLocalName().equals("file"))
						output.setFile(DOMUtil.getNodeValue(outputChild));
				}
			}
		}
		return output;
	}

	/**
	 * This method is used to parse the XML input nodes into Hawk Test configuration
	 * 
	 * @param  A XML node with input information
	 * @return An input object
	 */
	private Input parseInputElement(Node inputNode)
	{
		Input input = new Input();
		if (inputNode.hasChildNodes())
		{
			NodeList inputChildList = inputNode.getChildNodes();
			for (int i=0; i < inputChildList.getLength(); i++)
			{
				Node inputChild = inputChildList.item(i);
				if (inputChild.getLocalName() != null)
				{
					if (inputChild.getLocalName().equals("parameter"))
					{
						NamedNodeMap attributesList = inputChild.getAttributes();
						Node attribute = null;
						if ((attribute = attributesList.getNamedItem("value")) != null)
						{
							Value value = new Value(DOMUtil.getNodeValue(attribute));
							input.addParameter(value);
						}
						else
						{
							if ((attribute = attributesList.getNamedItem("variable")) != null) {
								Variable variable = new Variable(null);
								variable.setName(DOMUtil.getNodeValue(attribute));
								input.addParameter(variable);
							}
							else
							{
								if ((attribute = attributesList.getNamedItem("file")) != null) {
									File file = new File(null);
									file.setName(DOMUtil.getNodeValue(attribute));
									input.addParameter(file);
								}
							}
						}
					}
				}
			}
		}
		return input;
	}
}